package ogmios.components;
import java.util.*;
/**
 * An index of objects. The index allows you to go back
 * and forth through entries in order of insertion. Upon adding a new element,
 * the index will set its 'pointer' to the new element.
 */
public class HistoryIndex<E>
{
 public static final int RESET_OPERATION  = 0;
 public static final int BACK_OPERATION  = 1;
 public static final int FORWARD_OPERATION = 2;

 private LinkedList<E> index;
 private int limit;
 private int lastOperation;
 private ListIterator<E> iterator;
 public static final int DEFAULT_LIMIT = 10;

 /**
 * Creates a new HistoryIndex with the default limit of indices.
 */
 public HistoryIndex()
 {
   this(DEFAULT_LIMIT);
 }

 /**
 * Creates a new HistoryIndex
 */
 public HistoryIndex(int limit)
 {
  index = new LinkedList<E>();
  setLimit(limit);
  resetPointer();
 }

 /**
 * Resets the pointer to the most updated list iterator
 */
 public void resetPointer()
 {
   iterator = index.listIterator(index.size());
   lastOperation = RESET_OPERATION;
 }

 /**
 * Returns the index of the object returned by goBack()
 */
 public int getPreviousIndex()
 {
   return iterator.previousIndex();
 }

 /**
 * Returns the index of the object returned by goForward()
 */
 public int getNextIndex()
 {
  return iterator.nextIndex();
 }

 /**
 * Returns if there is another index before the pointer
 */
 public boolean canGoBack()
 {
  return iterator.hasPrevious();
 }

 /**
 * Returns if there is another index after the pointer.
 */
 public boolean canGoForward()
 {
  return iterator.hasNext();
 }

 /**
 * Returns the index before the pointer
 */
 public E goBack()
 {
   lastOperation = BACK_OPERATION;
   return iterator.previous();
 }

 /**
 * Returns the index after the pointer
 */
 public E goForward()
 {
   lastOperation = FORWARD_OPERATION;
   return iterator.next();
 }
 
 /**
 * Sets the limit on number of indices to keep
 */
 public void setLimit(int limit)
 {
   this.limit = limit;
   trim();
 }

 /**
 * Resizes the index if it is larger than the limit, and will return true if it had to resize.
 */
 protected boolean trim()
 {
   if (limit > -1)
   {
     int size = index.size();
     if (size > limit)
     {
       for (int i = size - limit - 1; i != -1; i--)
       {
         index.remove();
       }
       resetPointer();
       return true;
     }
   }
   return false;
 }

 /**
 * Returns last operation (either RESET_OPERATION, BACK_OPERATION, OR FORWARD_OPERATION);
 */
 public int getLastOperation()
 {
   return lastOperation;
 }

 /**
 * Returns the limt on number of indices to keep
 */
 public int getLimit()
 {
  return limit;
 }

 /**
 * Adds an index to the end
 */
 public void add(E index)
 {
   this.index.offer(index);
   if (!trim())
    resetPointer();
 }

 /**
 * Set the current index to a new value. This method will not work if the last operation was RESET_OPERATION.
 */
 public void setCurrentIndex(E newValue)
 {
   iterator.set(newValue);
 }

 /**
 * Returns the size of the index
 */
 public int size()
 {
   return index.size();
 }

}
